#include "voice_aiui.h"

REIListener* g_pListener = nullptr; // NOLINT

IAIUIAgent* g_pAgent = nullptr;

string gSyncSid;
string gVoiceCloneResId;
int gPcmPlayerIndex = -1;

REIListener::REIListener()
{
    // 创建内置的pcm播放器，并初始化，设置回调，启动起来
    aiui_pcm_player_create();
    int count = aiui_pcm_player_get_output_device_count();
    for (int i = 0; i < count; i++) {
        cout << "pcm player index: " << i
                << " device name: " << aiui_pcm_player_get_device_name(i) << endl;
    }
    cout << "user pcm player index: " << gPcmPlayerIndex << endl;
    aiui_pcm_player_init(gPcmPlayerIndex);
    // aiui_pcm_player_set_callbacks(
    //     onStarted, onPaused, onResumed, onStopped, onProgress, onError);
    aiui_pcm_player_start();
    std::shared_ptr<TtsHelperListener> listener = std::make_shared<TtsHelperListener>();
    m_pTtsHelper = std::make_shared<StreamNlpTtsHelper>(listener);
    m_pTtsHelper->setTextMinLimit(20);
}

void REIListener::onEvent(const IAIUIEvent& event)
{
    try {
        handleEvent(event);
    } catch (std::exception& e) {
        cout << e.what() << endl;
    }
}

void REIListener::TtsHelperListener::onText(const StreamNlpTtsHelper::OutTextSeg& textSeg) {
if (textSeg.isBegin() || textSeg.isEnd()) {
        if (aiui_pcm_player_get_state() != PCM_PLAYER_STATE_STARTED) {
            aiui_pcm_player_start();
        }
        if (textSeg.isBegin()) {
            aiui_pcm_player_clear();
        }
    }

    // 调用合成+
    startTTS(textSeg.mText, textSeg.mTag);
}

void REIListener::TtsHelperListener::onFinish(const string& fullText) {
    // 文本合成完成回调
    cout << "tts, fullText=" << fullText << endl;
}

void REIListener::TtsHelperListener::onTtsData(const Json::Value& bizParamJson, const char* audio, int len) {
    const Json::Value& data = (bizParamJson["data"])[0];
    const Json::Value& content = (data["content"])[0];
    int dts = content["dts"].asInt();
    int progress = content["text_percent"].asInt();
    // 将合成数据写入播放器
    aiui_pcm_player_write(0, audio, len, dts, progress);
}

void REIListener::processIntentJson(Json::Value& params,
                    Json::Value& intentJson,
                    std::string& resultStr,
                    int eosRsltTime,
                    std::string& sid)
{
    int rc = intentJson["rc"].asInt();
    Json::Value answerJson = intentJson["answer"];
    string answer = answerJson["text"].asString();
    // 正常nlp结果（AIUI通用语义模型返回的语义结果）
    cout << "----------------------------------" << endl;
    cout << "params: " << params.asString() << endl;
    cout << "nlp: " << resultStr << endl;
    cout << "eos_result=" << eosRsltTime << "ms" << endl;
    cout << "结果解析：" << endl;
    cout << "sid=" << sid << endl;
    cout << "text（请求文本）: " << intentJson["text"].asString() << endl;
    cout << "rc=" << rc << ", answer（应答语）: " << answer << endl;
}

void REIListener::handleEvent(const IAIUIEvent& event)
{
    switch (event.getEventType()) {
        // SDK状态
        case AIUIConstant::EVENT_STATE: {
            switch (event.getArg1()) {
                case AIUIConstant::STATE_IDLE: {
                    // 空闲状态，即最初始的状态
                    cout << "EVENT_STATE: STATE_IDLE" << endl;
                } break;

                case AIUIConstant::STATE_READY: {
                    // 准备好状态（待唤醒），可以进行唤醒
                    cout << "EVENT_STATE: STATE_READY" << endl;
                } break;

                case AIUIConstant::STATE_WORKING: {
                    // 工作状态（即已唤醒状态），可以语音交互，也可以再次唤醒
                    cout << "EVENT_STATE: STATE_WORKING" << endl;
                } break;
            }
        } break;
        // 唤醒事件
        case AIUIConstant::EVENT_WAKEUP: {
            cout << "EVENT_WAKEUP: " << event.getInfo() << endl;
            aiui_pcm_player_stop(); // 唤醒时停止播放
        } break;

        // 休眠事件，即一段时间无有效交互或者外部主动要求，SDK会自动进入STATE_READY状态
        case AIUIConstant::EVENT_SLEEP: {
            // arg1用来区分休眠类型，是自动休眠还是外部要求，可参考AIUIConstant.h中EVENT_SLEEP的注释
            cout << "EVENT_SLEEP: arg1=" << event.getArg1() << endl;
        } break;

        // VAD事件，如语音活动检测
        case AIUIConstant::EVENT_VAD: {
            // arg1为活动类型
            switch (event.getArg1()) {
                case AIUIConstant::VAD_BOS_TIMEOUT: {
                    cout << "EVENT_VAD: VAD_BOS_TIMEOUT" << endl;
                } break;

                // 检测到前端点，即开始说话
                case AIUIConstant::VAD_BOS: {
                    cout << "EVENT_VAD: BOS" << endl;
                } break;

                // 检测到后端点，即说话结束
                case AIUIConstant::VAD_EOS: {
                    cout << "EVENT_VAD: EOS" << endl;
                } break;

                // 音量，arg2为音量级别（0-30）
                case AIUIConstant::VAD_VOL: {
                    //cout << "EVENT_VAD: vol=" << event.getArg2() << endl;
                } break;
            }
        } break;
        // 结果事件
        case AIUIConstant::EVENT_RESULT: {
            Json::Value bizParamJson;
            Json::Reader reader;

            if (!reader.parse(event.getInfo(), bizParamJson, false)) {
                cout << "parse error! info=" << event.getInfo() << endl;
                break;
            }

            Json::Value& data = (bizParamJson["data"])[0];
            Json::Value& params = data["params"];
            Json::Value& content = (data["content"])[0];

            string sub = params["sub"].asString();
            if (sub != "iat" && sub != "nlp" && sub != "tts" && sub != "cbm_tidy" && sub != "cbm_semantic") {
                return;
            }

            // sid即唯一标识一次会话的id
            string sid = event.getData()->getString("sid", "");
            if (sub == "iat") {
                if (sid != mCurIatSid) {
                    cout << "**********************************" << endl;
                    cout << "sid=" << sid << endl;
                    mCurIatSid = sid;
                    // 新的会话，清空之前识别缓存
                    mIatTextBuffer.clear();
                    mStreamNlpAnswerBuffer.clear();
                    m_pTtsHelper->clear();
                    mIntentCnt = 0;
                }
            } else if (sub == "tts") {
                if (sid != mCurTtsSid) {
                    cout << "**********************************" << endl;
                    cout << "sid=" << sid << endl;
                    mTtsLen = 0;
                    mCurTtsSid = sid;
                }
            }

            Json::Value empty;
            string cnt_id = content.get("cnt_id", empty).asString();

            int dataLen = 0;

            // 注意：当buffer里存字符串时也不是以0结尾，当使用C语言时，转成字符串则需要自已在末尾加0
            const char* buffer = event.getData()->getBinary(cnt_id.c_str(), &dataLen);

            if (sub == "tts") {
                // 语音合成结果，返回url或者pcm音频
                cout << "tts: " << content.toString() << endl;
                Json::Value&& isUrl = content.get("url", empty);
                if (isUrl.asString() == "1") {
                    // 云端返回的是url链接，可以用播放器播放
                    cout << "tts_url=" << string(buffer, dataLen) << endl;
                } else {
                    // 云端返回的是pcm音频，分成一块块流式返回
                    int progress = 0;
                    int dts = content["dts"].asInt();

                    string tag = event.getData()->getString("tag", "");
                    if (tag.find("stream_nlp_tts") == 0) {
                        // 流式语义应答的合成
                        m_pTtsHelper->onOriginTtsData(tag, bizParamJson, buffer, dataLen);
                    } else {
                        // 只有碰到开始块和(特殊情况:合成字符比较少时只有一包tts，dts = 2)，开启播放器
                        if (dts == AIUIConstant::DTS_BLOCK_FIRST
                            || dts == AIUIConstant::DTS_ONE_BLOCK ||
                            (dts == AIUIConstant::DTS_BLOCK_LAST && 0 == mTtsLen)) {

                            if (aiui_pcm_player_get_state() != PCM_PLAYER_STATE_STARTED) {
                                int ret = aiui_pcm_player_start();
                                cout << "pcm start ret:" << ret << endl;
                            }
                        }
                        // 保存合成音频
                        mTtsLen += dataLen;
                        aiui_pcm_player_write(0, buffer, dataLen, dts, progress);
                                            // 音频开始
                        if (dts == AIUIConstant::DTS_BLOCK_FIRST ||
                            dts == AIUIConstant::DTS_ONE_BLOCK ||
                            /* 特殊情况:合成字符比较少时只有一包数据而且状态是２ */
                            (dts == AIUIConstant::DTS_BLOCK_LAST && 0 == mTtsLen)) {
                            mFs.open("tts.pcm", ios::binary | ios::out);
                        }

                        if (dataLen > 1) {
                            mFs.write(buffer, dataLen);
                        }

                        // 音频结束
                        if (dts == AIUIConstant::DTS_BLOCK_LAST || dts == AIUIConstant::DTS_ONE_BLOCK) {
                            mFs.close();
                        }
                    }
                } 
            } else if (sub == "iat") {
                // 语音识别结果
                string resultStr = string(buffer, dataLen);     // 注意：这里不能用string resultStr = buffer，因为buffer不一定以0结尾
                Json::Value resultJson;
                if (reader.parse(resultStr, resultJson, false)) {
                    Json::Value textJson = resultJson["text"];
                    bool isWpgs = false;
                    if (textJson.isMember("pgs")) {
                        isWpgs = true;
                    }

                    if (isWpgs) {
                        mIatTextBuffer = IatResultUtil::parsePgsIatText(textJson);
                    } else {
                        // 结果拼接起来
                        mIatTextBuffer.append(IatResultUtil::parseIatResult(textJson));
                    }

                    // 是否是该次会话最后一个识别结果
                    bool isLast = textJson["ls"].asBool();
                    if (isLast) {
                        cout << "params: " << params.asString() << endl;
                        cout << "iat: " << mIatTextBuffer << endl;

                        mIatTextBuffer.clear();
                    }
                }
            } else if (sub == "nlp") {
                // 语义理解结果
                // 注意：这里不能用string resultStr = buffer，因为buffer不一定以0结尾
                string resultStr = string(buffer, dataLen);

                // 从说完话到语义结果返回的时长
                long eosRsltTime = event.getData()->getLong("eos_rslt", -1);

                Json::Value resultJson;
                if (reader.parse(resultStr, resultJson, false)) {
                    // 判断是否为有效结果
                    if (resultJson.isMember("intent") &&
                        resultJson["intent"].isMember("rc")) {
                        // AIUI v1的语义结果
                        Json::Value intentJson = resultJson["intent"];
                        processIntentJson(params, intentJson, resultStr, eosRsltTime, sid);
                    } else if (resultJson.isMember("nlp")) {
                        // AIUI v2的语义结果
                        Json::Value nlpJson = resultJson["nlp"];
                        string text = nlpJson["text"].asString();

                        if (text.find("{\"intent\":") == 0) {
                            // 通用语义结果
                            Json::Value textJson;
                            if (reader.parse(text, textJson, false)) {
                                Json::Value intentJson = textJson["intent"];
                                processIntentJson(params, intentJson, resultStr, eosRsltTime, sid);
                            }
                        } else {
                            // 大模型语义结果
                            // 流式nlp结果里面有seq和status字段
                            int seq = nlpJson["seq"].asInt();
                            int status = nlpJson["status"].asInt();

                            if (status == 0) {
                                mStreamTtsIndex = 0;
                            }

                            /* 多意图取最后一次问题的结果进行tts合成 */
                            if (mIntentCnt > 1) {
                                int currentIntentIndex = 0;
                                Json::Value metaNlpJson;
                                Json::Value textJson = resultJson["cbm_meta"].get("text", metaNlpJson);
                                if (reader.parse(textJson.asString(), metaNlpJson, false)) {
                                    currentIntentIndex = metaNlpJson["nlp"]["intent"].asInt();
                                    if ((mIntentCnt - 1) != currentIntentIndex) {
                                        cout << "ignore nlp:" << resultStr << endl;
                                        return;
                                    }
                                } else {
                                    cout << "ignore nlp:" << resultStr << endl;
                                    return;
                                }
                            }
                            cout << "----------------------------------" << endl;
                            cout << "params: " << params.asString() << endl;
                            cout << "nlp: " << resultStr << endl;

                            if (seq == 0) {
                                long eosRsltTime = event.getData()->getLong("eos_rslt", -1);
                                cout << "eos_result=" << eosRsltTime << "ms" << endl;
                            }

                            cout << "结果解析：" << endl;
                            cout << "sid=" << sid << endl;
                            cout << "seq=" << seq << ", status=" << status << ", answer（应答语）: " << text << endl;
                            cout << "fullAnswer=" << (mStreamNlpAnswerBuffer.append(text)) << endl;

                            if (status == 2) {
                                mStreamNlpAnswerBuffer.clear();
                            }
                        }
                    } else {
                        // 无效结果，把原始结果打印出来
                        cout << "----------------------------------" << endl;
                        cout << "nlp: " << resultStr << endl;
                        cout << "sid=" << sid << endl;
                    }
                }
            } else if (sub == "cbm_tidy") {
                // 意图拆分的结果
                string intentStr = string(buffer, dataLen); // 注意：这里不能用string resultStr = buffer，因为buffer不一定以0结尾
                Json::Value tmpJson;
                if (reader.parse(intentStr, tmpJson, false)) {
                    Json::Value intentTextJson = tmpJson["cbm_tidy"]["text"];
                    if (!intentTextJson.empty() &&
                        reader.parse(intentTextJson.asString(), tmpJson, false)) {
                        mIntentCnt = tmpJson["intent"].size();
                        cout << "cbm_intent_cnt: " << mIntentCnt
                                << " text: " << tmpJson.toString() << endl;
                    }
                }
            } else {
                // 其他结果
                string resultStr = string(buffer, dataLen);     // 注意：这里不能用string resultStr = buffer，因为buffer不一定以0结尾

                cout << sub << ": " << event.getInfo() << endl << resultStr << endl;
            }
        } break;
        // 开始录音事件
        case AIUIConstant::EVENT_START_RECORD: {
            cout << "EVENT_START_RECORD " << endl;
        } break;

        // 停止录音事件
        case AIUIConstant::EVENT_STOP_RECORD: {
            cout << "EVENT_STOP_RECORD " << endl;
        } break;

        // 出错事件
        case AIUIConstant::EVENT_ERROR: {
            // 打印错误码和描述信息
            cout << "EVENT_ERROR: error=" << event.getArg1() << ", des=" << event.getInfo() << endl;
        } break;

        // 连接到服务器
        case AIUIConstant::EVENT_CONNECTED_TO_SERVER: {
            // 获取uid（为客户端在云端的唯一标识）并打印
            string uid = event.getData()->getString("uid", "");

            cout << "EVENT_CONNECTED_TO_SERVER, uid=" << uid << endl;
        } break;

        // 与服务器断开连接
        case AIUIConstant::EVENT_SERVER_DISCONNECTED: {
            cout << "EVENT_SERVER_DISCONNECTED " << endl;
        } break;

        //唤醒词操作的结果
        case AIUIConstant::EVENT_CAE_WAKEUP_WORD_RESULT: {
            int type = event.getArg1();
            int ret = event.getArg2();

            //保存唤醒词资源
            if (0 == ret && (type == AIUIConstant::CAE_GEN_WAKEUP_WORD ||
                                type == AIUIConstant::CAE_ADD_WAKEUP_WORD)) {
                int dataLen = 0;
                const char* buffer = event.getData()->getBinary("data", &dataLen);
                fstream fs;
                fs.open("./wakeup_word_res.bin", ios::binary | ios::out);
                fs.write(buffer, dataLen);
                fs.close();
            }

            if (ret != 0) {
                int dataLen = 0;
                const char* buffer = event.getData()->getBinary("error_msg", &dataLen);
                // 注意：这里不能用string errorMsg = buffer，因为buffer不一定以0结尾
                string errorMsg = string(buffer, dataLen);
                cout << "wakeup word operate fail, type = " << type << ", error code = " << ret
                        << ", error msg: " << errorMsg << endl;
            } else {
                cout << "wakeup word operate success, type = " << type << endl;
            }

        } break;

        //唤醒音频事件
        case AIUIConstant::EVENT_CAE_WAKEUP_AUDIO: {
            int type = event.getArg1();
            //保存唤醒音频
            if (type == AIUIConstant::CAE_WAKEUP_AUDIO) {;
                int dataLen = 0;
                const char* buffer = event.getData()->getBinary("audio", &dataLen);
                fstream fs;
                fs.open("wakeup_audio.pcm", ios::binary | ios::out);
                fs.write(buffer, dataLen);
                fs.close();
            }
        } break;
    };
}

/**
 * 测试语音合成，返回pcm数据。
 *
 * @param text
 */
int startTTS(const string& text, const string& tag)
{
    AIUIBuffer textData = aiui_create_buffer_from_data(text.c_str(), text.length());
    string params = "voice_name=x5_lingxiaoyue_flow";
    if (!tag.empty()) {
        params.append(",tag=").append(tag);
    }
    if (!g_pAgent){
        // 使用发音人x4_lingxiaoying_em_v2合成，也可以使用其他发音人                                                   
        IAIUIMessage* msg = IAIUIMessage::create(AIUIConstant::CMD_TTS, AIUIConstant::START, 0, params.c_str(), textData); 
        g_pAgent->sendMessage(msg);                                              
        msg->destroy();
        return 1;
    }
    return 0;
}

/**
 * 测试流式语音合成，即分段上传文本，返回pcm数据。
 *
 * @param text 待合成文本
 * @param dts 数据流状态，取值：0（第一块数据），1（中间数据），2（最后一块数据）
 */
int startTTS(const string& text, int dts, const string& tag)
{
    AIUIBuffer textData = aiui_create_buffer_from_data(text.c_str(), text.length());
    char buffer[10] = {0};
    snprintf(buffer, sizeof(buffer), "%d", dts);

    string params = "voice_name=x5_lingxiaoyue_flow,data_status=" + std::string(buffer);
    if (!tag.empty()) {
        params.append(",tag=").append(tag);
    }
    if (!g_pAgent){
        // 使用发音人x4_lingxiaoying_em_v2合成，也可以使用其他发音人                                                   
        IAIUIMessage* msg = IAIUIMessage::create(AIUIConstant::CMD_TTS, AIUIConstant::START, 0, params.c_str(), textData); 
        g_pAgent->sendMessage(msg);                                              
        msg->destroy();
        return 1;
    }
    return 0; 
}

string readFileAsString(const string& path)
{
    ifstream t(path, ios_base::in | ios::binary);
    if (!t.is_open()) {
        std::cout << "Error open file: " << path << " fail." << endl;
        return "";
    }
    string str((istreambuf_iterator<char>(t)), istreambuf_iterator<char>());

    return str;
}

void createAgent(bool more, const char* cfgPath)
{
    if (g_pAgent) {
        return;
    }
    string aiuiParams = readFileAsString(cfgPath);
    Json::Value paramJson;
    Json::Reader reader;
    if (reader.parse(aiuiParams, paramJson, false)) {
        if (more) {
            cout << paramJson.toString() << endl;
        }
        g_pListener->mMoreDetails = more;
        g_pAgent = IAIUIAgent::createAgent(paramJson.toString().c_str(), g_pListener);
        cout<<"创建aiui成功"<<endl;
    }
    if (!g_pAgent) {
        std::cout << string(cfgPath) << ", " << reader.getFormatedErrorMessages() << std::endl;
        cout<<"创建aiui失败"<<endl;
        return;
    }
}

/**
 * 销毁AIUIAgent对象。
 */
void destroyAgent()
{
    if (g_pAgent) {
        g_pAgent->destroy();
        g_pAgent = nullptr;
    }
}



int main(int argc,char* argv[]){
    return 0;
}